#include <ncurses.h>
#include <string>
#include <fstream>
#include <stdio.h>
using namespace std;
template <typename T>
class stack
{
private:
    struct Node
    {
        T value;
        Node *next;
    };
    Node *topNode;
    int _size = 0;

public:
    stack() : topNode(nullptr) {}
    ~stack()
    {
        clear();
    }
    bool empty() const
    {
        return topNode == nullptr;
    }
    void push(const T &value)
    {
        Node *newNode = new Node{value, topNode};
        topNode = newNode;
        _size++;
    }
    T top()
    {
        if (empty())
            throw "Stack::top(): empty stack";
        return topNode->value;
    }
    void pop()
    {
        if (empty())
            throw "Stack::pop(): empty stack";
        Node *temp = topNode;
        topNode = topNode->next;
        delete temp;
        _size--;
    }
    void clear()
    {
        while (!empty())
            pop();
    }
    int size() const
    {
        return _size;
    }
};
class erow
{
public:
    erow(const string &text = "") : _text(text) {}
    int size() const
    {
        return _text.size();
    }
    bool insert(char ch, int pos)
    {
        if (pos < 0 || pos > size())
            return false;
        _text.insert(pos, 1, ch);
        return true;
    }
    bool erase(int pos)
    {
        if (pos < 0 || pos >= size())
            return false;
        _text.erase(pos, 1);
        return true;
    }
    void replace(const string &target, const string &replacement)
    {
        size_t position = 0;
        while ((position = _text.find(target, position)) != std::string::npos)
        {
            _text.replace(position, target.length(), replacement);
            position += replacement.length();
        }
    }
    string text() const
    {
        return _text;
    }
    void set_text(const string &text)
    {
        _text = text;
    }

private:
    string _text;
};
class document
{
public:
    document()
    {
    }
    document(const document &d)
    {
        file_name = d.file_name;
        capacity = d.capacity;
        _size = 0;
        _char_count = 0;
        rows = new erow[capacity];
        for (int i = 0; i < d.size(); i++)
            insert_row(d.get(i), i);
    }
    document(const string &file_name) : file_name(file_name)
    {
        capacity = 128;
        rows = new erow[capacity];
        _size = 0;
        _char_count = 0;
    }
    ~document()
    {
        delete[] rows;
    }
    bool insert_row(erow row, int pos)
    {
        if (pos < 0 || pos > _size)
            return false;
        if (_size == capacity)
            resize(capacity * 2);
        for (int i = _size; i > pos; i--)
            rows[i] = rows[i - 1];
        rows[pos] = row;
        _size++;
        _char_count += row.size();
        return true;
    }
    bool insert_char_at(char ch, int row, int pos)
    {
        if (row < 0 || row >= _size)
            return false;
        if (rows[row].insert(ch, pos))
        {
            _char_count++;
            return true;
        }
        return false;
    }
    bool erase_row(int row)
    {
        if (row < 0 || row >= _size)
            return false;
        for (int i = row; i < _size - 1; i++)
            rows[i] = rows[i + 1];
        _size--;
        _char_count -= rows[row].size();
        return true;
    }
    bool erase_char_at(int row, int pos)
    {
        if (row < 0 || row >= _size)
            return false;
        if (rows[row].erase(pos))
        {
            _char_count--;
            return true;
        }
        return false;
    }
    void enter(int row, int pos)
    {
        string str = rows[row].text();
        rows[row].set_text(str.substr(0, pos));
        insert_row(erow(str.substr(pos, str.size() - pos + 1)), row + 1);
    }
    void replace(const string &target, const string &replacement)
    {
        _char_count = 0;
        for (int i = 0; i < _size; i++)
        {
            rows[i].replace(target, replacement);
            _char_count += rows[i].size();
        }
    }
    void set_row(const string &text, int row)
    {
        rows[row].set_text(text);
    }
    void clear()
    {
        _size = 0;
        _char_count = 0;
    }
    int size() const
    {
        return _size;
    }
    bool save() const
    {
        ofstream outfile(file_name);
        if (!outfile.is_open())
            return false;
        for (int i = 0; i < _size; i++)
            outfile << rows[i].text() << endl;
        outfile.close();
        return true;
    }
    bool open()
    {
        ifstream infile(file_name);
        if (!infile.good())
        {
            ofstream outfile(file_name);
            insert_row(erow(""), 0);
            outfile.close();
        }
        else
        {
            string line;
            int i = 0;
            while (getline(infile, line))
            {
                insert_row(erow(line), i++);
            }
            infile.close();
        }
        if (_size == 0)
            insert_row(erow(""), 0);
        return true;
    }
    long char_count() const
    {
        return _char_count;
    }
    erow get(int index) const
    {
        if (index < 0 || index >= _size)
            return erow("");
        return rows[index];
    }
    string name() const
    {
        return file_name;
    }

private:
    void resize(int new_size)
    {
        erow *new_rows = new erow[new_size];
        for (int i = 0; i < _size; i++)
            new_rows[i] = rows[i];
        delete[] rows;
        rows = new_rows;
        capacity = new_size;
    }
    int capacity, _size;
    long _char_count;
    erow *rows;
    string file_name;
};
enum mode
{
    NORMAL = 1,
    INSERT = 2,
    SEARCH = 3,
    REPLACE = 4
};
enum color
{
    WHITE = 0,
    RED = 1,
    YELLOW = 2,
    BULUE = 3
};
class editor
{
public:
    editor()
    {
        init();
    }
    ~editor()
    {
        endwin();
    }
    void run(const string &file_name)
    {
        document d(file_name);
        if (!d.open())
        {
            throw "can't open the file";
            return;
        }
        push_undo_stack(d);
        clear_redo_stack();
        saved = false;
        while (!_exit)
        {
            get_window_size(width, height);
            erase();
            print_msg(d);
            print_document(d);
            move(cy - rowoffset, cx);
            refresh();
            int ch = getch();
            switch (_mode)
            {
            case NORMAL:
                handle_normal_input(ch, d);
                break;
            case INSERT:
                handle_insert_input(ch, d);
                break;
            case SEARCH:
                handle_search_input(ch, d);
                break;
            case REPLACE:
                handle_replace_input(ch, d);
                break;
            default:
                break;
            }
        }
        endwin();
    }

private:
    struct editor_state
    {
        document d;
        int rowoffset, cx, cy;
        editor_state(document d = document(), int rowoffset = 0, int cx = 0, int cy = 0) : d(d), rowoffset(rowoffset), cx(cx), cy(cy) {}
    };
    void init()
    {
        initscr();
        cbreak();
        noecho();
        keypad(stdscr, TRUE);
        start_color();
        get_window_size(width, height);
        int background = getbkgd(stdscr);
        short color_pair = background & A_COLOR;
        short color = color_pair & 0xFF;
        init_pair(WHITE, COLOR_WHITE, color);
        init_pair(YELLOW, COLOR_YELLOW, color);
        init_pair(RED, COLOR_RED, color);
        init_pair(BULUE, COLOR_BLUE, color);
        _mode = NORMAL;
        cx = 0;
        cy = 0;
        rowoffset = 0;
        _exit = false;
        target = "";
        cmd_buffer = "";
    }
    void get_window_size(int &width, int &height)
    {
        getmaxyx(stdscr, height, width);
        height -= 2;
    }
    void handle_normal_input(int input, document &d)
    {
        switch (input)
        {
        case 'h':
        case KEY_LEFT:
            left(d);
            break;
        case 'j':
        case KEY_DOWN:
            down(d);
            break;
        case 'k':
        case KEY_UP:
            up(d);
            break;
        case 'l':
        case KEY_RIGHT:
            right(d);
            break;
        case 'i':
            switch_mode(INSERT);
            break;
        case 'u':
            undo(d);
            saved = false;
            break;
        case 'r':
            redo(d);
            saved = false;
            break;
        case 'y':
            coppy_row(d);
            break;
        case 'p':
            pasete_row(d);
            push_undo_stack(d);
            clear_redo_stack();
            saved = false;
            break;
        case 'd':
            erase_row(d);
            push_undo_stack(d);
            clear_redo_stack();
            saved = false;
            break;
        case 'w':
            save(d);
            break;
        case 'q':
            exit();
            break;
        case 'x':
            erase_char(d);
            push_undo_stack(d);
            clear_redo_stack();
            saved = false;
            break;
        case '/':
            switch_mode(SEARCH);
            break;
        case 't':
            switch_mode(REPLACE);
            break;
        }
    }
    void handle_insert_input(int input, document &d)
    {
        switch (input)
        {
        case 27: // Esc
            switch_mode(NORMAL);
            break;
        case KEY_UP:
            up(d);
            break;
        case KEY_DOWN:
            down(d);
            break;
        case KEY_LEFT:
            left(d);
            break;
        case KEY_RIGHT:
            right(d);
            break;
        case KEY_BACKSPACE:
            erase_char(d);
            push_undo_stack(d);
            clear_redo_stack();
            saved = false;
            break;
        case 10: // enter
            enter(d);
            push_undo_stack(d);
            clear_redo_stack();
            saved = false;
            break;
        default:
            insert_char(input, d);
            push_undo_stack(d);
            clear_redo_stack();
            saved = false;
            break;
        }
    }
    void handle_search_input(int input, document &d)
    {
        switch (input)
        {
        case KEY_BACKSPACE:
            if (target.size() > 0)
                target.pop_back();
            break;
        case 27:
        case 10:
            switch_mode(NORMAL);
            break;
        default:
            target += (char)(input);
            break;
        }
    }
    void handle_replace_input(int input, document &d)
    {
        switch (input)
        {
        case 27: // Esc
            cmd_buffer.clear();
            target.clear();
            switch_mode(NORMAL);
            break;
        case 10: // Enter
            replace(d);
            cmd_buffer.clear();
            target.clear();
            switch_mode(NORMAL);
            break;
        case KEY_BACKSPACE:
            if (cmd_buffer.size() > 0)
                cmd_buffer.pop_back();
            break;
        default:
            cmd_buffer += (char)(input);
            break;
        }
        int idx = cmd_buffer.size() - 1;
        while (idx >= 0 && cmd_buffer[idx] != '/')
            idx--;
        if (idx < 0)
            target = cmd_buffer;
        else
            target = cmd_buffer.substr(0, idx);
    }
    void replace(document &d)
    {
        int idx = cmd_buffer.size() - 1;
        while (idx >= 0 && cmd_buffer[idx] != '/')
            idx--;
        if (idx < 0)
            return;
        d.replace(target, cmd_buffer.substr(idx + 1, cmd_buffer.size() - idx));
        push_undo_stack(d);
        clear_redo_stack();
        saved = false;
    }
    void switch_mode(mode m)
    {
        _mode = m;
    }
    void enter(document &d)
    {
        d.enter(cy, cx);
        cx = 0;
        down(d);
    }
    void erase_char(document &d)
    {
        if (cx == 0 && cy > 0)
        {
            cx = d.get(cy - 1).size();
            d.set_row(d.get(cy - 1).text() + d.get(cy).text(), cy - 1);
            d.erase_row(cy);
            up(d);
            return;
        }
        d.erase_char_at(cy, cx - 1);
        cx = _max(cx - 1, 0);
    }
    void insert_char(char ch, document &d)
    {
        if (cx == width)
            return;
        d.insert_char_at(ch, cy, cx);
        cx++;
    }
    void coppy_row(const document &d)
    {
        coppy = d.get(cy).text();
    }
    void pasete_row(document &d)
    {
        if (coppy == "")
            return;
        d.insert_row(erow(coppy), cy + 1);
    }
    void erase_row(document &d)
    {
        d.erase_row(cy);
        if (d.size() == 0)
        {
            d.insert_row(erow(""), 0);
        }
        if (cy == d.size())
        {
            up(d);
        }
        // cx = _min(cx, d.get(cy).size());
    }
    void exit()
    {
        if (!saved)
        {
            mvprintw(height, 0, "unsaved,press 'q' to quit");
            move(cy - rowoffset, cx);
            int ch = getch();
            if (ch == 'q')
                _exit = true;
        }
        else
            _exit = true;
    }
    void up(const document &d)
    {
        if (cy == rowoffset)
            rowoffset = _max(rowoffset - 1, 0);
        cy = _max(cy - 1, 0);
        cx = _min(cx, d.get(cy).size());
    }
    void left(const document &d)
    {
        if (cx == 0)
        {
            cx = width;
            up(d);
        }
        else
            cx--;
    }
    void down(const document &d)
    {
        if (cy == rowoffset + height - 1)
            rowoffset = _min(rowoffset + 1, d.size() - 1);
        cy = _min(cy + 1, d.size() - 1);
        cx = _min(cx, d.get(cy).size());
    }
    void right(const document &d)
    {
        if (cx == d.get(cy).size())
        {
            cx = 0;
            down(d);
        }
        else
            cx++;
    }
    void print_document(const document &d)
    {
        int row = 0;
        for (row = 0; row < height && row + rowoffset < d.size(); row++)
        {
            string s = d.get(row + rowoffset).text();
            mvprintw(row, 0, "%s", s.c_str());
            if (target != "")
            {
                int n = s.size();
                int m = target.size();
                int *next = new int[m];
                next[0] = 0;
                for (int j = 0, i = 1; i < m; i++)
                {
                    while (target[i] != target[j] && j > 0)
                        j = next[j - 1];
                    if (target[i] == target[j])
                        j++;
                    next[i] = j;
                }
                for (int i = 0, j = 0; i < n; i++)
                {
                    while (s[i] != target[j] && j > 0)
                        j = next[j - 1];
                    if (s[i] == target[j])
                        j++;
                    if (j == m)
                    {
                        attron(COLOR_PAIR(YELLOW));
                        mvprintw(row, i - m + 1, "%s", target.c_str());
                        attroff(COLOR_PAIR(YELLOW));
                        j = 0;
                    }
                }
                delete[] next;
            }
        }
        while (row < height)
        {
            attron(COLOR_PAIR(BULUE));
            mvprintw(row++, 0, "~");
            attroff(COLOR_PAIR(BULUE));
        }
    }
    void print_msg(const document &d)
    {
        string mode_str = _mode == NORMAL ? "--Normal--" : (_mode == INSERT ? "--Insert--" : (_mode == SEARCH ? "--Search--" : "--Replace--"));
        mvprintw(height + 1, 0, "%s", mode_str.c_str());
        mvprintw(height + 1, width - 6, "%d", d.size());
        mvprintw(height + 1, width - 7, ",");
        mvprintw(height + 1, width - 12, "%ld", d.char_count());
        mvprintw(height + 1, width - 13 - d.name().size(), "%s", d.name().c_str());
        if (!saved)
            mvprintw(height + 1, width - 14 - d.name().size(), "*");
        if (_mode == SEARCH)
            mvprintw(height, 0, "%s", target.c_str());
        else if (_mode == REPLACE)
            mvprintw(height, 0, "%s", cmd_buffer.c_str());
    }
    void save(const document &d)
    {
        saved = d.save();
    }
    void undo(document &d)
    {
        if (undo_stack.size() > 1)
        {
            redo_stack.push(undo_stack.top());
            undo_stack.pop();
            editor_state t = undo_stack.top();
            d.clear();
            for (int i = 0; i < t.d.size(); i++)
            {
                d.insert_row(t.d.get(i), i);
            }
            rowoffset = t.rowoffset, cx = t.cx, cy = t.cy;
        }
    }
    void redo(document &d)
    {
        if (!redo_stack.empty())
        {
            push_undo_stack(d);
            editor_state t = redo_stack.top();
            redo_stack.pop();
            d.clear();
            for (int i = 0; i < t.d.size(); i++)
                d.insert_row(t.d.get(i), i);
            rowoffset = t.rowoffset, cx = t.cx, cy = t.cy;
        }
    }
    void push_undo_stack(document &d)
    {
        undo_stack.push(editor_state(document(d), rowoffset, cx, cy));
    }
    void clear_redo_stack()
    {
        redo_stack.clear();
    }
    mode _mode;
    int rowoffset, width, height, cx, cy;
    bool saved, _exit;
    string target, cmd_buffer, coppy;
    inline int _max(int a, int b) { return a > b ? a : b; }
    inline int _min(int a, int b) { return a < b ? a : b; }
    stack<editor_state> redo_stack;
    stack<editor_state> undo_stack;
};
void help()
{
    printf("i-------.insert\n");
    printf("d-------.delete line\n");
    printf("y-------.copy line\n");
    printf("p-------.paste line\n");
    printf("u-------.undo\n");
    printf("r-------.redo\n");
    printf("/-------.search\n");
    printf("t-------.replace\n");
    printf("x-------.delete char\n");
}
int main(int argc, char **argv)
{
    if (argc == 1)
    {
        printf("error: you must type a filename, use \"vjm -help\" for more information\n");
        return 0;
    }
    string s = string(argv[1]);
    if (s == "-help")
    {
        help();
        return 0;
    }
    editor e;
    e.run(argv[1]);
    return 0;
}
// #include <ncurses.h>
// #include <string>
// #include <fstream>
// #include <math.h>
// using namespace std;
// struct Vector2
// {
//     int x;
//     int y;
//     Vector2(int x = 0, int y = 0) : x(x), y(y) {}
// };
// template <typename T>
// class Stack
// {
// public:
//     Stack() : top(-1), capacity(16), data(new T[16]) {}
//     ~Stack() { delete[] data; }
//     void push(const T &t)
//     {
//         if (top == capacity - 1)
//             resize(capacity * 2);
//         data[top++] = t;
//     }
//     T peek() { return data[top]; }
//     T pop() { return data[top--]; }
//     bool empty()
//     {
//         return top == -1;
//     }

// private:
//     void resize(int newCapacity)
//     {
//         capacity = newCapacity;
//         T *newData = new T[newCapacity];
//         for (int i = 0; i <= top; i++)
//         {
//             newData[i] = data[i];
//         }
//         delete[] newData;
//         data = newData;
//     }
//     int top;
//     int capacity;
//     T *data;
// };
// template <typename T>
// class List
// {
// public:
//     List()
//     {
//         _size = 0;
//         capacity = 16;
//         data = new T[capacity];
//     }
//     ~List() { delete[] data; }
//     void add(const T &t) { insert(t, _size); }
//     void insert(const T &t, int index)
//     {
//         if (_size == capacity)
//             resize(capacity * 2);
//         for (int i = _size; i > index; i--)
//             data[i] = data[i - 1];
//         data[index] = t;
//         _size++;
//     }
//     void erase(int index)
//     {
//         for (int i = index; i < _size - 1; i++)
//             data[i] = data[i + 1];
//         _size--;
//     }
//     inline T get(int index) const { return data[index]; }
//     inline int size() const { return _size; }
//     inline void set(const T &t, int index)
//     {
//         data[index] = t;
//     }
//     bool empty() const { return _size == 0; }
//     void clear() { _size = 0; }

// private:
//     void resize(int newCapacity)
//     {
//         capacity = newCapacity;
//         T *newData = new T[capacity];
//         for (int i = 0; i < _size; i++)
//         {
//             newData[i] = data[i];
//         }
//         delete[] data;
//         data = newData;
//     }
//     int _size;
//     int capacity;
//     T *data;
// };
// class Document
// {
// public:
//     Document(string name = "") : _charCount(0), _name(name), rows(List<string>()) {}
//     Document(const Document &o)
//     {
//         _name = o._name;
//         rows = List<string>();
//         for (int i = 0; i < o.rows.size(); i++)
//             addRow(o.rows.get(i));
//     }
//     void addRow(const string &s) { insertRow(s, rows.size()); }
//     void insertRow(const string &s, int row)
//     {
//         rows.insert(s, row);
//         _charCount += s.size();
//     }
//     void insertChar(char ch, int row, int pos)
//     {
//         string s = rows.get(row);
//         s.insert(pos, 1, ch);
//         setRow(s, row);
//         _charCount++;
//     }
//     void setRow(const string &s, int row)
//     {
//         _charCount -= rows.get(row).size();
//         rows.set(s, row);
//         _charCount += s.size();
//     }
//     void eraseRow(int row)
//     {
//         _charCount -= rows.get(row).size();
//         rows.erase(row);
//     }
//     void eraseChar(int row, int pos)
//     {
//         string s = rows.get(row);
//         s.erase(pos, 1);
//         setRow(s, row);
//         _charCount--;
//     }
//     bool empty() const { return rows.empty(); }
//     List<Vector2> kmp(const string &t)
//     {
//         List<Vector2> res = List<Vector2>();
//         int m = t.size();
//         string s;
//         int *next = new int[m];
//         next[0] = 0;
//         for (int i = 1, j = 0; i < m; i++)
//         {
//             while (j > 0 && t[i] != t[j])
//                 j = next[j - 1];
//             if (t[i] == t[j])
//                 j++;
//             next[i] = j;
//         }
//         for (int y = 0; y < rows.size(); y++)
//         {
//             s = rows.get(y);
//             int n = s.size();
//             for (int x = 0, j = 0; x < n; x++)
//             {
//                 while (j > 0 && s[x] != s[j])
//                     j = next[j - 1];
//                 if (s[x] == t[j])
//                     j++;
//                 if (j == m)
//                 {
//                     res.add(Vector2(x, y));
//                     j = 0;
//                 }
//             }
//         }
//         return res;
//     }
//     inline long charCount() const { return _charCount; }
//     inline int rowCount() const { return rows.size(); }
//     string name() const { return _name; }
//     string getRow(int row) const { return rows.get(row); }

// private:
//     long _charCount;
//     string _name;
//     List<string> rows;
// };

// class Vjm
// {
// public:
//     Vjm(const string &filename)
//     {
//         init(filename);
//     }
//     ~Vjm()
//     {
//     }
//     void run()
//     {
//     }

// private:
//     void init(const string &filename)
//     {
//         initscr();
//         cbreak();
//         noecho();
//         keypad(stdscr, TRUE);
//         start_color();
//         e = EditorState(Document(filename));
//         // open();
//     }
//     void up()
//     {
//         if (e.curPos.y == e.rowoffset)
//             e.rowoffset = max(e.rowoffset - 1, 0);
//         e.curPos.y = max(e.curPos.y - 1, 0);
//         e.curPos.x = min(e.curPos.x, (int)e.d.getRow(e.curPos.y).size());
//     }
//     void left()
//     {
//         if (e.curPos.x == 0 && e.curPos.y > 0)
//         {
//             e.curPos.x = e.d.getRow(e.curPos.y - 1).size();
//             up();
//         }
//         else if (e.curPos.x > 0)
//             e.curPos.x--;
//     }
//     void right()
//     {
//         if (e.curPos.x == (int)e.d.getRow(e.curPos.y).size() && e.curPos.y < e.d.rowCount())
//         {
//             e.curPos.x = 0;
//             down();
//         }
//         else if (e.curPos.x < (int)e.d.getRow(e.curPos.y).size())
//             e.curPos.x++;
//     }
//     void down()
//     {
//         if (e.curPos.y == e.rowoffset + height - 1)
//             e.rowoffset = min(e.rowoffset + 1, e.d.rowCount() - 1);
//         e.curPos.y = min(e.curPos.y + 1, e.d.rowCount() - 1);
//         e.curPos.x = min(e.curPos.x, (int)e.d.getRow(e.curPos.y).size());
//     }
//     void insertRow(const string &s)
//     {
//         e.d.insertRow(s, e.curPos.y);
//         down();
//     }
//     void insertChar(char ch)
//     {
//         if ((int)e.d.getRow(e.curPos.y).size() >= width)
//             return;
//         e.d.insertChar(ch, e.curPos.y, e.curPos.x);
//     }
//     void eraseRow()
//     {
//         e.d.eraseRow(e.curPos.y);
//         if (e.d.empty())
//             e.d.addRow("");
//         up();
//     }
//     void eraseChar()
//     {
//         if (e.curPos.x == 0 && e.curPos.y > 0)
//         {
//             e.curPos.x = e.d.getRow(e.curPos.y - 1).size();
//             e.d.setRow(e.d.getRow(e.curPos.y - 1) + e.d.getRow(e.curPos.y), e.curPos.y - 1);
//             e.d.eraseRow(e.curPos.y);
//             up();
//         }
//     }
//     void open()
//     {
//         ifstream infile(e.d.name());
//         if (!infile.good())
//         {
//             ofstream outfile(e.d.name());
//             outfile.close();
//         }
//         else
//         {
//             string line;
//             while (getline(infile, line))
//                 e.d.addRow(line);
//             infile.close();
//         }
//         if (e.d.empty())
//             e.d.addRow("");
//     }
//     void save()
//     {
//         ofstream outfile(e.d.name());
//         if (!outfile.is_open())
//             return;
//         for (int i = 0; i < e.d.rowCount(); i++)
//             outfile << e.d.getRow(i) << endl;
//         outfile.close();
//     }

//     struct EditorState
//     {
//         Document d;
//         int rowoffset = 0;
//         Vector2 curPos = Vector2(0, 0);
//         EditorState() {}
//         EditorState(const Document &d)
//         {
//             this->d = Document(d);
//         }
//     };
//     enum Mode
//     {
//         NORMAL,
//         INSERT,
//         REPLACE,
//         SEARCH
//     };
//     int width, height;
//     EditorState e;
//     Stack<EditorState> undoStack, redoStack;
// };
// int main()
// {
//     Vjm v = Vjm("hello.txt");
//     // v.run();
//     while (true)
//     {
//         }

//     return 0;
// }